<?php
/**
 *  微信支付接口
 *
 * @author  camfee <camfee@foxmail.com>
 */

namespace controls\MobileApi\Payment;


use Center\CoinLog;
use Center\Goods;
use Center\Payment;
use Center\User;
use Common\DataType;
use MobileApi\ApiBase;
use lib\plugins\weixin\PayApi;
use lib\plugins\weixin\ResultData;
use lib\plugins\weixin\AppPackageData;

/**
 * 微信支付 相关接口
 *
 * @package Payment
 * @author  camfee <camfee@foxmail.com>
 *
 */
class Weixin extends ApiBase
{
    /**
     * 创建预支付订单
     *
     * <pre>
     * POST
     *      product_id: 购买商品的id
     * </pre>
     *
     * @return string|void json
     *
     * <pre>
     * {
     *      "Status": 200,
     *      "Result": {
     *          "Sn" : "支付流水号",
     *          "PrepayId": "微信支付订单预交易号",
     *          "Package": {  //客户端调用支付接口的签名包,
     *              "appid" : "appid",
     *              "partnerid" : "商户号",
     *              "prepayid" : "订单预交易号",
     *              "packageValue" : "Sign=WXPay", //由于java那边不能用package，所以改成了packageValue
     *              "noncestr" : "随机支付传",
     *              "timestamp" : "时间戳",
     *              "sign": "签名",
     *          }
     *      }
     * }
     *  异常情况
     * 201: 购买的商品不存在
     * 212: 网络错误
     * 213: 数据错误
     * </pre>
     */
    public function create()
    {
        $uid = $this->isLogin(true);
        $product_id = trim($_POST['product_id']);
        $app_id = $GLOBALS['g_appid'];
        $device_id = $GLOBALS['g_deviceid'];
        $channel = $GLOBALS['g_channel'];

        $product_info = Goods::getInfoByIds($product_id);
        if (empty($product_info)) {
            debug_log("Weixin buy {$product_id} not exist");
            $this->output(201, '购买商品不存在');
        }
        $goods_title = $product_info['Title'];
        $price = $product_info['Price'] * 100;
        $sn = Payment::generateSN($app_id, Payment::PAY_TYPE_WEIXIN, $channel);

        //发起api请求
        $weixin_config = loadconf('payment/payment')['weixin'];
        $weixin_pay = new PayApi($weixin_config);
        $res = $weixin_pay->unified([
            "out_trade_no" => $sn,
            "body" => $goods_title,
            "total_fee" => $price,
            "notify_url" => loadconf('payment/payment')['weixin']['NotifyUrl'],
            "trade_type" => 'APP',
            'spbill_create_ip' => self::clientIp(),
        ]);
        if (empty($res)) {
            debug_log("Weixin buy request unified error", JF_LOG_WARNING);
            $this->output(212, '网络错误1');
        }
        $weixin_result = ResultData::parseFromXml($res);
        if ($weixin_result === false) {
            debug_log('weixin buy request unified result error', JF_LOG_WARNING);
            $this->output(212, '网络错误2');
        }
        $msg = 'OK';
        if ($weixin_result->checkSign($weixin_pay->getConfig()->getKey()) === false || $weixin_result->checkResult($msg) === false) {
            debug_log("weixin buy unified response error, {$msg}", JF_LOG_WARNING);
            $this->output(212, '网络错误3');
        }
        //创建payments model
        $prepay_id = $weixin_result->getValue('prepay_id');
        $params = [
            'AppId' => $app_id,
            'DeviceId' => $device_id,
            'UserId' => $uid,
            'GoodsInfo' => $goods_title,
            'SN' => $sn,
            'ProductId' => $product_id,
            'PaymentType' => Payment::PAY_TYPE_WEIXIN,
            'TradeNo' => $prepay_id,
            'TotalFee' => $price,
            'Status' => Payment::STATUS_WAIT_PAY,
        ];
        $ret = Payment::add($params);
        if ($ret === false) {
            debug_log(["weixin buy addPayment failed, ret: {$ret}", $params], JF_LOG_ERROR);
            $this->output(213, '数据错误');
        }

        debug_log("weixin buy addPayment, uid: {$uid}, appId: {$app_id}, productId: {$product_id}");
        //获取请求结果，生成app调用支付签名包
        $app_package_data = new AppPackageData($weixin_config, $prepay_id);
        $this->output(200, [
            'Sn' => $sn,
            "PrepayId" => $prepay_id,
            'Package' => $app_package_data->generatePackageParams()
        ]);
    }

    /**
     * 查询订单
     *
     * <pre>
     * POST
     *      sn: 必选， 交易流水号
     * </pre>
     *
     * @return string json
     *
     * <pre>
     * {
     *      "Status": 200,
     *      "Result": {}
     * }
     * 异常情况
     * 211: 请求参数错误
     * 212: 订单不存在
     * 213: 用户已取消支付
     * 214: 支付失败
     * 215: 网络错误
     * 216: 微信接口错误
     * 217: 正在支付当中 （客户端应该继续查询）
     * </pre>
     */
    public function query()
    {
        $uid = (int)$this->isLogin();
        $sn = $_POST['sn'];
        if (empty($sn)) {
            $this->output(211, '参数错误');
        }
        $payment_info = Payment::getInfoBySN($sn);
        if (empty($payment_info)) {
            $this->output(212, '订单不存在');
        }

        $payment_id = $payment_info['PaymentId'];
        $product_id = $payment_info['ProductId'];
        $product_info = Goods::getInfoByIds($product_id);
        if (empty($product_info)) {
            debug_log("weixin pay query productId:{$product_id} not exist", JF_LOG_ERROR);
            $this->output(212, '订单异常');
        }
        $old_status = $payment_info['Status'];
        if ($old_status == Payment::STATUS_PAY_CANCELED) {
            $this->output(213, '用户已取消支付');
        } elseif ($old_status == Payment::STATUS_PAY_FAILURE) {
            $this->output(214, '用户支付失败');
        } elseif ($old_status == Payment::STATUS_PAY_SUCCESS) {
            $this->output(200, '支付成功');
        }

        //发起api请求
        $weixin_config = loadconf('payment/payment')['weixin'];
        $weixin_pay = new PayApi($weixin_config);
        $res = $weixin_pay->query([
            'out_trade_no' => $sn
        ]);
        if (empty($res)) {
            debug_log("weixin pay request query failed, paymentId:{$payment_id}", JF_LOG_WARNING);
            $this->output(215, '网络错误1');
        }
        $weixin_result = ResultData::parseFromXml($res);
        if ($weixin_result === false) {
            debug_log("weixin pay query result error, paymentId:{$payment_id}", JF_LOG_WARNING);
            $this->output(215, '网络错误2');
        }
        $msg = 'OK';
        if ($weixin_result->checkSign($weixin_pay->getConfig()->getKey()) === false) {
            debug_log("weixin pay query result sign error, msg:{$msg}", JF_LOG_WARNING);
            $this->output(216, '接口返回错误');
        }
        if ($weixin_result->checkResult($msg) === false) {
            $this->output(217, "正在支付中, {$msg}}");
        }
        $trade_status = $weixin_result->getValue('trade_state');
        if ($trade_status === 'USERPAYING') {
            $this->output(217, '正在支付中');

        } elseif ($trade_status === 'SUCCESS') {
            if (Payment::isFirstPay($uid)) {
                CoinLog::addCoinLog($uid, 2, CoinLog::TYPE_FIRST_PAY);
            }
            $data = [
                'ThirdSN' => $weixin_result->getValue('transaction_id'),
                'Content' => $res,
                'Status' => Payment::STATUS_PAY_SUCCESS,
                'UpdateTime' => DataType::datetime(),
            ];
            if (Payment::paySuccess($sn, $data) !== false) {
                $user_id = $payment_info['UserId'];
                $coin = (int)$product_info['JCoin'] + (int)$product_info['Donate'];
                pay_succ_log("paySuccess, paymentId:{$payment_id}, userId:{$user_id}");
                if (User::updateJCoin($user_id, $coin) === false) {
                    pay_fail_log("addBean failed, paymentId:{$payment_id}, userId:{$user_id}, Coin:{$coin}");
                    Payment::updatePaymentBySN($sn, ['Status' => Payment::STATUS_SEND_GOODS_FAILED]);
                } else {
                    CoinLog::addCoinLog($uid, $product_info['JCoin'], CoinLog::TYPE_EXCHANGE, false);
                    if (!empty($product_info['Donate'])) {
                        CoinLog::addCoinLog($uid, $product_info['Donate'], CoinLog::TYPE_PAY_DONATE, false);
                    }
                    pay_succ_log("addBean, paymentId:{$payment_id}, userId:{$user_id}, Coin:{$coin}");
                }
            } else {
                pay_fail_log("paySuccess failed, paymentId:{$payment_id}, uid:{$uid}");
            }
            $this->output(200, '支付成功');

        } elseif ($trade_status === 'REVOKED') {
            debug_log("weixin pay query failed, paymentId:{$payment_id}, status:{$trade_status}", JF_LOG_WARNING);
            Payment::updatePaymentBySN($sn, ['Status' => Payment::STATUS_PAY_CANCELED]);
            $this->output(213, $weixin_result->getValue('trade_state_desc'));

        } else {
            debug_log("weixin pay query failed, paymentId:{$payment_id}, status:{$trade_status}", JF_LOG_WARNING);
            Payment::updatePaymentBySN($sn, ['Status' => Payment::STATUS_PAY_FAILURE]);
            $this->output(214, $weixin_result->getValue('trade_state_desc'));
        }
        exit;
    }
}